package com.huilian.iotbox.data.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.huilian.iotbox.data.dao.CommodityStockCommonDao;
import com.huilian.iotbox.data.dto.*;
import com.huilian.iotbox.data.enums.CommodityStockRecordFirstTypeEnum;
import com.huilian.iotbox.data.enums.CommodityStockRecordSecondTypeEnum;
import com.huilian.iotbox.data.po.User;
import com.huilian.iotbox.data.service.*;
import com.huilian.iotbox.data.utils.RedisCache;
import com.huilian.iotbox.data.utils.Util;
import com.huilian.iotbox.data.vo.*;
import com.huilian.tool.common.config.WeixinConfig;
import com.huilian.tool.redis.ToolRedisKey;
import com.huilian.tool.weixin.service.WeixinService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Service
@Slf4j
public class CommodityStockCommonServiceImpl implements CommodityStockCommonService {
    /**
     * 最高失败次数
     */
    private final static int COUNT = 10;

    @Autowired
    private CommodityStockCommonDao commodityStockDao;
    @Resource
    private CommodityCommonService commodityService;
    @Resource
    private CommodityStockRecordCommonService commodityStockRecordService;
    @Resource
    private CommodityStockRecordItemCommonService commodityStockRecordItemService;
    @Resource
    private MerchantStoreCommonService merchantStoreService;
    @Resource
    private UserCommonService userCommonService;
    @Resource
    private WeixinConfig weixinConfig;
    @Resource
    private WeixinService weixinService;
    @Resource
    private RedisCache redisCache;


    @Override
    public List<CommodityStockVo> findStockData(CommodityStockDto commodityStockDto) {
        commodityStockDto.setUserId(commodityStockDto.getUserInfo().getId());
        commodityStockDto.setType(2);
        return commodityStockDao.getStock(commodityStockDto);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateSafeStock(CommodityStockDto commodityStockDto) throws Exception {
        if (ObjectUtils.isEmpty(commodityStockDto.getSafeStock())) {
            throw new Exception("请输入安全库存数量");
        }
        CommodityStockVo commodityStockVo = commodityStockDao.findById(commodityStockDto.getId());
        if (ObjectUtils.isEmpty(commodityStockVo)) {
            throw new Exception("该商品不存在");
        }
        commodityStockDto.setSafeStock(commodityStockDto.getSafeStock());
        commodityStockDto.setVersion(commodityStockVo.getVersion());
        int result = commodityStockDao.updateSafeStock(commodityStockDto);
        if (result < 1) {
            throw new Exception("数据修改失败");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void amendStock(CommodityStockDto commodityStockDto) throws Exception {

        if (ObjectUtils.isEmpty(commodityStockDto.getStock())) {
            throw new Exception("修改失败，请输入修正后库存数量");
        }
        if (ObjectUtils.isEmpty(commodityStockDto.getRetailPrice())) {
            throw new Exception("修改失败，请输入修正后销售价");
        }
        if (ObjectUtils.isEmpty(commodityStockDto.getCostPrice())) {
            throw new Exception("修改失败，请输入修正后成本价");
        }
        CommodityStockVo commodityStockVo = commodityStockDao.findById(commodityStockDto.getId());
        if (ObjectUtils.isEmpty(commodityStockVo)) {
            throw new Exception("修改失败，该商品不存在");
        }

        CommodityStockRecordDto commodityStockRecordDto = new CommodityStockRecordDto();
        commodityStockRecordDto.setDescription(ObjectUtils.isEmpty(commodityStockDto.getDescription()) ? "手动盘点" : commodityStockDto.getDescription());
        commodityStockRecordDto.setUserId(commodityStockDto.getUserInfo().getId());
        commodityStockRecordDto.setMerchantStoreId(commodityStockDto.getUserInfo().getMchStoreId());
        commodityStockRecordDto.setOutTradeNo(Util.getOutTradeNo());
        commodityStockRecordDto.setFirstType(CommodityStockRecordFirstTypeEnum.INVENTORY_SHEET.getCode());
        commodityStockRecordDto.setSecondaryType(CommodityStockRecordSecondTypeEnum.COMMODITY_INVENTORY.getCode());
        commodityStockRecordDto.setOperationUserId(commodityStockDto.getUserInfo().getId());
        commodityStockRecordService.add(commodityStockRecordDto);
        //如果库存数不一样 则生成盘亏或盘盈
        if (!commodityStockDto.getStock().equals(commodityStockVo.getStock())) {
            CommodityStockRecordItemDto commodityStockRecordItemDto = new CommodityStockRecordItemDto();
            commodityStockRecordItemDto.setCommodityStockRecordId(commodityStockRecordDto.getId());
            commodityStockRecordItemDto.setCommodityId(commodityStockVo.getCommodityId());
            commodityStockRecordItemDto.setCommoditySupplierId(commodityStockVo.getCommoditySupplierId());
            commodityStockRecordItemDto.setQuantity(commodityStockDto.getStock());
            if (commodityStockDto.getStock() < commodityStockVo.getStock()) {
                commodityStockRecordItemDto.setRemark("盘亏");
            } else if (commodityStockDto.getStock() > commodityStockVo.getStock()) {
                commodityStockRecordItemDto.setRemark("盘盈");
            }
            commodityStockRecordItemDto.setCostPrice(commodityStockDto.getCostPrice());
            commodityStockRecordItemDto.setRetailPrice(commodityStockDto.getRetailPrice());
            commodityStockRecordItemDto.setStockBefore(commodityStockVo.getStock());
            commodityStockRecordItemDto.setStockAfter(commodityStockDto.getStock());
            commodityStockRecordItemDto.setCostPriceBefore(commodityStockDto.getCostPrice());
            commodityStockRecordItemDto.setCostPriceAfter(commodityStockVo.getCostPrice());
            commodityStockRecordItemDto.setRetailPriceBefore(commodityStockDto.getRetailPrice());
            commodityStockRecordItemDto.setRetailPriceAfter(commodityStockVo.getRetailPrice());
            commodityStockRecordItemService.add(commodityStockRecordItemDto);
        }


        CommodityStockDto commodityStockDtoV1 = new CommodityStockDto();
        commodityStockDtoV1.setStock(commodityStockDto.getStock());
        commodityStockDtoV1.setCostPrice(commodityStockDto.getCostPrice());
        commodityStockDtoV1.setRetailPrice(commodityStockDto.getRetailPrice());
        commodityStockDtoV1.setId(commodityStockVo.getId());
        commodityStockDtoV1.setDescription(commodityStockDto.getDescription());
        commodityStockDtoV1.setVersion(commodityStockVo.getVersion());
        int result = commodityStockDao.updateAmendStock(commodityStockDtoV1);
        if (result < 1) {
            throw new Exception("修改失败,请查看输入数据是否正确");
        }
    }

    @Override
    public void inOrOutStock(CommodityStockDto commodityStockDto) throws Exception {
        if (commodityStockDto.getTypeStock() == 0) {
            if (ObjectUtils.isEmpty(commodityStockDto.getAddStock())) {
                throw new Exception("修改失败，请输入入库数量");
            }
        } else {
            if (ObjectUtils.isEmpty(commodityStockDto.getAddStock())) {
                throw new Exception("修改失败，请输入出库数量");
            }
        }
        CommodityStockVo commodityStockVo = commodityStockDao.findById(commodityStockDto.getId());
        if (ObjectUtils.isEmpty(commodityStockVo)) {
            throw new Exception("修改失败，该商品不存在");
        }
        if (commodityStockDto.getTypeStock() == 1 && commodityStockVo.getStock() < commodityStockDto.getAddStock()) {
            throw new Exception("出库数量不能大于原有库存数");
        }

        CommodityStockRecordDto commodityStockRecordDto = new CommodityStockRecordDto();
        commodityStockRecordDto.setUserInfo(commodityStockDto.getUserInfo());
        commodityStockRecordDto.setMerchantStoreId(commodityStockVo.getMerchantStoreId());
        List<Long> commodityIdList = new ArrayList<>();
        commodityIdList.add(commodityStockVo.getCommodityId());
        CommodityDto commodityDto = new CommodityDto();
        commodityDto.setUserInfo(commodityStockDto.getUserInfo());
        commodityDto.setCommodityIdList(commodityIdList);
        commodityDto.setMerchantStoreId(commodityStockVo.getMerchantStoreId());
        List<CommodityVo> commodityVoList = commodityService.findListById(commodityDto);
        List<CommodityDto> displayRackItemDtos = new ArrayList<>();
        for (CommodityVo commodityVo : commodityVoList) {
            CommodityDto commodityDto1 = new CommodityDto();
            commodityDto1.setId(commodityVo.getId());
            commodityDto1.setEtStock(commodityStockDto.getAddStock());
            commodityDto1.setCommoditySupplierId(commodityStockDto.getCommoditySupplierId());
            commodityDto1.setCostPrice(commodityVo.getCostPrice());
            commodityDto1.setRetailPrice(commodityVo.getRetailPrice());
            commodityDto1.setEtCostPrice(commodityVo.getCostPrice());
            commodityDto1.setEtRetailPrice(commodityVo.getRetailPrice());
            commodityDto1.setStock(commodityVo.getStock());
            displayRackItemDtos.add(commodityDto1);
        }
        commodityStockRecordDto.setDisplayRackItemDtos(displayRackItemDtos);
        commodityStockRecordDto.setFirstType(commodityStockDto.getFirstType());
        if (commodityStockDto.getTypeStock() == 0) {
            commodityStockRecordService.intoRecord(commodityStockRecordDto);
        } else if (commodityStockDto.getTypeStock() == 1) {
            commodityStockRecordService.outRecord(commodityStockRecordDto);
        }
    }

    @Override
    public int updateAmendStockByCommodityIdAdd(CommodityStockDto commodityStockDto) {

        CommodityStockVo commodityStockVo = commodityStockDao.findCommodityStock(commodityStockDto);
        BigDecimal costPriceBefore = commodityStockVo.getCostPrice().multiply(BigDecimal.valueOf(commodityStockVo.getStock()));
        BigDecimal retailPriceBefore = commodityStockVo.getRetailPrice().multiply(BigDecimal.valueOf(commodityStockVo.getStock()));
        BigDecimal addCostPrice = commodityStockDto.getAddCostPrice().multiply(BigDecimal.valueOf(commodityStockDto.getAddStock()));
        BigDecimal addRetailPrice = commodityStockDto.getAddRetailPrice().multiply(BigDecimal.valueOf(commodityStockDto.getAddStock()));
        Integer stock = commodityStockVo.getStock() + commodityStockDto.getAddStock();
        BigDecimal costPrice = costPriceBefore.add(addCostPrice);
        BigDecimal retailPrice = retailPriceBefore.add(addRetailPrice);
        commodityStockDto.setId(commodityStockVo.getId());
        commodityStockDto.setStock(stock);

        if (stock == 0) {
            commodityStockDto.setCostPrice(costPrice);
            commodityStockDto.setRetailPrice(retailPrice);
        } else {
            commodityStockDto.setCostPrice(costPrice.divide(BigDecimal.valueOf(stock), 2, RoundingMode.HALF_UP));
            commodityStockDto.setRetailPrice(retailPrice.divide(BigDecimal.valueOf(stock), 2, RoundingMode.HALF_UP));
        }
//        commodityStockDto.setMerchantStoreId(commodityStockVo.getMerchantStoreId());
//        commodityStockDto.setSupplierId(commodityStockVo.getCommoditySupplierId());
//        commodityStockDto.setCommodityId(commodityStockVo.getCommodityId());
        commodityStockDto.setVersion(commodityStockVo.getVersion());
        return commodityStockDao.updateAmendStock(commodityStockDto);
    }

    @Override
    public int updateAmendStockByCommodityIdMinus(CommodityStockDto commodityStockDto) {
        CommodityStockVo commodityStockVo = commodityStockDao.findCommodityStock(commodityStockDto);
        BigDecimal costPriceBefore = commodityStockVo.getCostPrice().multiply(BigDecimal.valueOf(commodityStockVo.getStock()));
        BigDecimal retailPriceBefore = commodityStockVo.getRetailPrice().multiply(BigDecimal.valueOf(commodityStockVo.getStock()));
        BigDecimal addCostPrice = commodityStockDto.getAddCostPrice().multiply(BigDecimal.valueOf(commodityStockDto.getAddStock()));
        BigDecimal addRetailPrice = commodityStockDto.getAddRetailPrice().multiply(BigDecimal.valueOf(commodityStockDto.getAddStock()));
        Integer stock = commodityStockVo.getStock() - commodityStockDto.getAddStock();
        BigDecimal costPrice = costPriceBefore.subtract(addCostPrice);
        BigDecimal retailPrice = retailPriceBefore.subtract(addRetailPrice);
        commodityStockDto.setId(commodityStockVo.getId());
        if (stock <= 0) {
            commodityStockDto.setStock(stock);
            commodityStockDto.setCostPrice(BigDecimal.ZERO);
            commodityStockDto.setRetailPrice(BigDecimal.ZERO);
        } else {
            commodityStockDto.setStock(stock);
            commodityStockDto.setCostPrice(costPrice.divide(BigDecimal.valueOf(stock), 2, RoundingMode.HALF_UP));
            commodityStockDto.setRetailPrice(retailPrice.divide(BigDecimal.valueOf(stock), 2, RoundingMode.HALF_UP));
        }

//        commodityStockDto.setMerchantStoreId(commodityStockVo.getMerchantStoreId());
//        commodityStockDto.setSupplierId(commodityStockVo.getCommoditySupplierId());
//        commodityStockDto.setCommodityId(commodityStockVo.getCommodityId());
        commodityStockDto.setVersion(commodityStockVo.getVersion());
        return commodityStockDao.updateAmendStock(commodityStockDto);
    }

    @Override
    public int add(CommodityStockDto commodityStockDto) {
        return commodityStockDao.add(commodityStockDto);
    }

    @Override
    public CommodityStockVo findStockV2(CommodityStockDto commodityStockDto) {
        CommodityStockVo data = commodityStockDao.findStockV2(commodityStockDto);
        if (ObjectUtils.isEmpty(data)) {
            data = new CommodityStockVo();
            data.setStock(0);
        }
        return data;
    }

    @Override
    public List<CommodityStockVo> findByCommodity(CommodityStockDto commodityStockDto) {
        commodityStockDto.setMerchantStoreId(commodityStockDto.getUserInfo().getMchStoreId());
        return commodityStockDao.findByCommodity(commodityStockDto);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void inventory(CommodityStockDto commodityStockDto) throws Exception {
        UserInfo userInfo = commodityStockDto.getUserInfo();

        List<CommodityStockDto> commodityStockList = commodityStockDto.getCommodityStockDtoList();
        if (commodityStockList == null || commodityStockList.size() == 0) {
            throw new Exception("没有盘点单数据，无法生成！");
        }

        CommodityStockRecordDto commodityStockRecordDto = new CommodityStockRecordDto();
        commodityStockRecordDto.setMerchantStoreId(userInfo.getMchStoreId());
        commodityStockRecordDto.setUserId(userInfo.getMchStoreUserId());
        commodityStockRecordDto.setOutTradeNo(Util.getOutTradeNo());
        commodityStockRecordDto.setDescription("手动盘点");
        commodityStockRecordDto.setFirstType(CommodityStockRecordFirstTypeEnum.INVENTORY_SHEET.getCode());
        commodityStockRecordDto.setSecondaryType(CommodityStockRecordSecondTypeEnum.COMMODITY_INVENTORY.getCode());
        commodityStockRecordDto.setOperationUserId(userInfo.getId());
        commodityStockRecordService.add(commodityStockRecordDto);

        CommodityStockRecordItemDto commodityStockRecordItemDto = new CommodityStockRecordItemDto();
        commodityStockRecordItemDto.setCommodityStockRecordId(commodityStockRecordDto.getId());

        for (CommodityStockDto stockDto : commodityStockList) {
            CommodityStockVo commodityStockVo = commodityStockDao.findById(stockDto.getId());

            if (ObjectUtils.isEmpty(commodityStockVo)) {
                throw new Exception("未找到对应的库存！");
            }

            commodityStockRecordItemDto.setCommodityId(commodityStockVo.getCommodityId());
            commodityStockRecordItemDto.setQuantity(stockDto.getQuantity());
            commodityStockRecordItemDto.setCostPrice(commodityStockVo.getCostPrice());
            commodityStockRecordItemDto.setRetailPrice(commodityStockVo.getRetailPrice());
            commodityStockRecordItemDto.setCommoditySupplierId(commodityStockVo.getCommoditySupplierId());
            commodityStockRecordItemDto.setStockBefore(commodityStockVo.getCommoditySupplierId());
            if (stockDto.getQuantity() < commodityStockVo.getStock()) {
                commodityStockRecordItemDto.setRemark("盘亏");
            } else if (stockDto.getQuantity() > commodityStockVo.getStock()) {
                commodityStockRecordItemDto.setRemark("盘盈");
            }
            stockDto.setDateOfInventory(new Date(System.currentTimeMillis()));

            this.updateStock(stockDto, commodityStockRecordItemDto, 0);
        }
    }

    @Override
    public List<CommodityStockVo> findInventoryNot(CommodityStockDto commodityStockDto) {
        commodityStockDto.setUserId(commodityStockDto.getUserInfo().getId());
        commodityStockDto.setType(2);
        List<CommodityStockVo> list = commodityStockDao.findStock(commodityStockDto);
        List<CommodityStockVo> listData = new ArrayList<>();
        for (CommodityStockVo commodityStockVo : list) {
            if (ObjectUtils.isEmpty(commodityStockVo.getDateOfInventory()) && commodityStockVo.getStock() > 0) {
                listData.add(commodityStockVo);
            }
        }
        return listData;
    }

    @Override
    public CommodityStockVo findCommodityStock(CommodityStockDto commodityStockDto) {
        return commodityStockDao.findCommodityStock(commodityStockDto);
    }

    /**
     * 扣商品库存（乐观锁）<br/>
     * 需要参数：<br/>
     * merchantStoreId <br/>
     * userId  <br/>
     * commodityId <br/>
     * minusStock <br/>
     * strictInventoryManagement 是否开启严格库存（开启后再判断商品有无开启）<br/>
     * description<br/>
     * firstType<br/>
     * secondaryType<br/>
     * operationUserId<br/>
     * retailPrice
     *
     * @param commodityStockDto
     * @throws Exception
     */
    @Override
    public void minusStockService(CommodityStockDto commodityStockDto, Integer type) throws Exception {
        //减库存
        CommodityStockDto updateCompleteCommodityStock = this.minusStock(commodityStockDto, 1);

        //减库存记录
        CommodityStockRecordDto addCommodityStockRecordDto = new CommodityStockRecordDto();
        addCommodityStockRecordDto.setUserId(commodityStockDto.getUserId());
        addCommodityStockRecordDto.setMerchantStoreId(commodityStockDto.getMerchantStoreId());
        addCommodityStockRecordDto.setOutTradeNo(Util.getOutTradeNo());
        addCommodityStockRecordDto.setDescription(commodityStockDto.getDescription());
        addCommodityStockRecordDto.setFirstType(commodityStockDto.getFirstType());
        addCommodityStockRecordDto.setSecondaryType(commodityStockDto.getSecondaryType());
        addCommodityStockRecordDto.setOperationUserId(commodityStockDto.getOperationUserId());
        commodityStockRecordService.add(addCommodityStockRecordDto);

        //减库存记录详情
        CommodityStockRecordItemDto addCommodityStockRecordItemDto = new CommodityStockRecordItemDto();
        addCommodityStockRecordItemDto.setCommodityStockRecordId(addCommodityStockRecordDto.getId());
        addCommodityStockRecordItemDto.setCommodityId(commodityStockDto.getCommodityId());
        addCommodityStockRecordItemDto.setQuantity(commodityStockDto.getMinusStock());
        String strType = "";
        if (type.equals(CommodityStockRecordSecondTypeEnum.SALE_OUT.getCode())) {
            strType = "销售出库";
        } else if (type.equals(CommodityStockRecordSecondTypeEnum.EXCHANGE_OUT.getCode())) {
            strType = "兑换出库";
        }
        addCommodityStockRecordItemDto.setRemark(ObjectUtils.isEmpty(commodityStockDto.getRemark()) ? strType : commodityStockDto.getRemark());
        if (!ObjectUtils.isEmpty(updateCompleteCommodityStock)) {
            addCommodityStockRecordItemDto.setCostPrice(updateCompleteCommodityStock.getCostPrice());
            addCommodityStockRecordItemDto.setRetailPrice(updateCompleteCommodityStock.getRetailPrice());
            addCommodityStockRecordItemDto.setStockBefore(updateCompleteCommodityStock.getStock() + commodityStockDto.getMinusStock());
            addCommodityStockRecordItemDto.setStockAfter(updateCompleteCommodityStock.getStock());
        } else {
            addCommodityStockRecordItemDto.setRetailPrice(commodityStockDto.getRetailPrice());
        }
        commodityStockRecordItemService.add(addCommodityStockRecordItemDto);
    }


    /**
     * 扣商品库存（乐观锁）<br/>
     * 需要参数：<br/>
     * merchantStoreId <br/>
     * userId  <br/>
     * commodityId <br/>
     * minusStock <br/>
     * strictInventoryManagement 是否开启严格库存（开启后再判断商品有无开启）
     *
     * @param commodityStockDto
     * @param runCount
     * @return
     * @throws Exception
     */
    private CommodityStockDto minusStock(CommodityStockDto commodityStockDto, int runCount) throws Exception {
        long begin = System.currentTimeMillis();

        if (ObjectUtils.isEmpty(commodityStockDto.getMinusStock())) {
            throw new Exception("需要扣减的库存数不能为空");
        }

        CommodityStockVo commodityStockVo = commodityStockDao.findByCommodityIdAndMerchantStoreId(commodityStockDto);

        if (!ObjectUtils.isEmpty(commodityStockVo)) {
            log.info("为commodityId:" + commodityStockDto.getId() + "减库存线程" + Thread.currentThread().getName() + "第" + runCount + "次执行");
            log.info("参数为：" + commodityStockDto);

            CommodityStockDto updateStockDto = new CommodityStockDto();
            updateStockDto.setId(commodityStockVo.getId());

            //外部传进来，是否按照严格库存模式扣，然后再判断商品是否开启严格库存模式
            if (!ObjectUtils.isEmpty(commodityStockDto.getStrictInventoryManagement()) && commodityStockDto.getStrictInventoryManagement()) {
                if (!ObjectUtils.isEmpty(commodityStockVo.getStrictInventoryManagement()) && commodityStockVo.getStrictInventoryManagement()) {
                    if (commodityStockDto.getMinusStock() > commodityStockVo.getStock()) {
                        throw new Exception(commodityStockVo.getCommodityName() + "  库存不足");
                    }
                }
            }

            updateStockDto.setStock(commodityStockVo.getStock() - commodityStockDto.getMinusStock());
            updateStockDto.setVersion(commodityStockVo.getVersion());
            int count = commodityStockDao.update(updateStockDto);

            commodityStockDto.setStock(updateStockDto.getStock());
            commodityStockDto.setCostPrice(commodityStockVo.getCostPrice());
            commodityStockDto.setRetailPrice(commodityStockVo.getRetailPrice());

            if (count > 0) {
                //安全库存，提示

                if (!ObjectUtils.isEmpty(commodityStockVo.getInventoryReminder()) && commodityStockVo.getInventoryReminder()) {
                    User user = userCommonService.findById(commodityStockDto.getUserId());
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("template_id", weixinConfig.getMessageTemplate());
                    jsonObject.put("touser", user.getOfficialAccountOpenId());
                    JSONObject data = new JSONObject();
                    JSONObject item = new JSONObject();
                    item.put("value", "请注意");
                    item.put("color", "");
                    data.put("first", item);
                    item.put("value", commodityStockVo.getCommodityName() + "库存已低于安全库存！请及时补充！");
                    data.put("performance", item);
                    item.put("value", sdf.format(new Date()));
                    data.put("time", item);
                    item.put("value", "请管理人员及时处理，谢谢！");
                    data.put("remark", item);
                    jsonObject.put("data", data);
                    log.info(JSON.toJSONString(jsonObject));
                    String errmsg = weixinService.SendTemplateMessage(jsonObject);
                    if (!"ok".equals(errmsg)) {
                        //token 失效,刷新Token重发一次
                        if (errmsg.contains("access_token is invalid or not latest hint")) {
                            final String key = String.format(ToolRedisKey.WEI_XIN_ACCESS_TOKEN, weixinConfig.getAppId());
                            redisCache.deleteObject(key);
                            errmsg = weixinService.SendTemplateMessage(jsonObject);
                            if (!"ok".equals(errmsg)) {
                                log.error("公众号通知发送失败：userId:" + user.getId());
                            }
                        }
                    } else {
                        log.info("公众号通知发送成功：userId:" + user.getId());
                    }

                }
                log.info("为commodityId:" + commodityStockDto.getId() + "减库存成功！业务耗时：" + (System.currentTimeMillis() - begin) + "ms");
                return commodityStockDto;
            } else {
                if (runCount <= COUNT) {
                    return this.minusStock(commodityStockDto, ++runCount);
                } else {
                    log.info("为commodityId:" + commodityStockDto.getId() + "减库存失败！commodityStockId为" + commodityStockVo.getId());
                    throw new Exception("为commodityId:" + commodityStockDto.getId() + "减库存失败！commodityStockId为" + commodityStockVo.getId());
                }
            }
        } else {
            return null;
        }
    }

    /**
     * 加商品库存（乐观锁）<br/>
     * 需要参数：<br/>
     * merchantStoreId <br/>
     * userId  <br/>
     * commodityId <br/>
     * addStock <br/>
     * description<br/>
     * firstType<br/>
     * secondaryType<br/>
     * operationUserId<br/>
     * retailPrice
     *
     * @param commodityStockDto
     * @throws Exception
     */
    @Override
    public void addStockService(CommodityStockDto commodityStockDto, Integer type) throws Exception {
        //加库存
        CommodityStockDto updateCompleteCommodityStock = this.addStock(commodityStockDto, 1);

        //加库存记录
        CommodityStockRecordDto addCommodityStockRecordDto = new CommodityStockRecordDto();
        addCommodityStockRecordDto.setUserId(commodityStockDto.getUserId());
        addCommodityStockRecordDto.setMerchantStoreId(commodityStockDto.getMerchantStoreId());
        addCommodityStockRecordDto.setOutTradeNo(Util.getOutTradeNo());
        addCommodityStockRecordDto.setDescription(commodityStockDto.getDescription());
        addCommodityStockRecordDto.setFirstType(commodityStockDto.getFirstType());
        addCommodityStockRecordDto.setSecondaryType(commodityStockDto.getSecondaryType());
        addCommodityStockRecordDto.setOperationUserId(commodityStockDto.getOperationUserId());
        commodityStockRecordService.add(addCommodityStockRecordDto);

        //加库存记录详情
        CommodityStockRecordItemDto addCommodityStockRecordItemDto = new CommodityStockRecordItemDto();
        addCommodityStockRecordItemDto.setCommodityStockRecordId(addCommodityStockRecordDto.getId());
        addCommodityStockRecordItemDto.setCommodityId(commodityStockDto.getCommodityId());
        addCommodityStockRecordItemDto.setQuantity(commodityStockDto.getAddStock());
        String strType = "";
        if (type.equals(CommodityStockRecordSecondTypeEnum.EXCHANGE_INTO.getCode())) {
            strType = "回收入库";
        }
        addCommodityStockRecordItemDto.setRemark(ObjectUtils.isEmpty(commodityStockDto.getRemark()) ? strType : commodityStockDto.getRemark());

        if (!ObjectUtils.isEmpty(updateCompleteCommodityStock)) {
            addCommodityStockRecordItemDto.setCostPrice(updateCompleteCommodityStock.getCostPrice());
            addCommodityStockRecordItemDto.setRetailPrice(updateCompleteCommodityStock.getRetailPrice());
            addCommodityStockRecordItemDto.setStockBefore(updateCompleteCommodityStock.getStock() - commodityStockDto.getAddStock());
            addCommodityStockRecordItemDto.setStockAfter(updateCompleteCommodityStock.getStock());
        } else {
            addCommodityStockRecordItemDto.setRetailPrice(commodityStockDto.getRetailPrice());
        }
        commodityStockRecordItemService.add(addCommodityStockRecordItemDto);
    }

    private CommodityStockDto addStock(CommodityStockDto commodityStockDto, int runCount) throws Exception {
        long begin = System.currentTimeMillis();

        if (ObjectUtils.isEmpty(commodityStockDto.getAddStock())) {
            throw new Exception("需要增加的库存数不能为空");
        }

        CommodityStockVo commodityStockVo = commodityStockDao.findByCommodityIdAndMerchantStoreId(commodityStockDto);

        log.info("为commodityId:" + commodityStockDto.getId() + "加库存线程" + Thread.currentThread().getName() + "第" + runCount + "次执行");
        log.info("参数为：" + commodityStockDto);
        CommodityStockDto updateStockDto = new CommodityStockDto();
        int count;
        if (ObjectUtils.isEmpty(commodityStockVo)) {
            updateStockDto.setUserId(commodityStockDto.getUserId());
            updateStockDto.setMerchantStoreId(commodityStockDto.getMerchantStoreId());
            updateStockDto.setCommodityId(commodityStockDto.getCommodityId());
            updateStockDto.setStock(1);
            updateStockDto.setInventoryReminder(false);
            updateStockDto.setCostPrice(BigDecimal.ZERO);
            updateStockDto.setRetailPrice(BigDecimal.ZERO);
            updateStockDto.setSafeStock(0);
            updateStockDto.setVersion(0);
            count = commodityStockDao.add(updateStockDto);
        } else {
            updateStockDto.setId(commodityStockVo.getId());
            updateStockDto.setStock(commodityStockVo.getStock() + commodityStockDto.getAddStock());
            updateStockDto.setVersion(commodityStockVo.getVersion());
            count = commodityStockDao.update(updateStockDto);
        }

        if (ObjectUtils.isEmpty(commodityStockVo)) {
            commodityStockDto.setStock(1);
            commodityStockDto.setCostPrice(BigDecimal.ZERO);
            commodityStockDto.setRetailPrice(BigDecimal.ZERO);
        } else {
            commodityStockDto.setStock(updateStockDto.getStock());
            commodityStockDto.setCostPrice(commodityStockVo.getCostPrice());
            commodityStockDto.setRetailPrice(commodityStockVo.getRetailPrice());
        }


        if (count > 0) {
            log.info("为commodityId:" + commodityStockDto.getId() + "加库存成功！业务耗时：" + (System.currentTimeMillis() - begin) + "ms");
            return commodityStockDto;
        } else {
            if (runCount <= COUNT) {
                return this.addStock(commodityStockDto, ++runCount);
            } else {
                log.info("为commodityId:" + commodityStockDto.getId() + "加库存失败！commodityStockId为" + commodityStockVo.getId());
                throw new Exception("为commodityId:" + commodityStockDto.getId() + "加库存失败！commodityStockId为" + commodityStockVo.getId());
            }
        }

    }

    private void updateStock(CommodityStockDto commodityStockDto, CommodityStockRecordItemDto commodityStockRecordItemDto, int num) throws Exception {
        CommodityStockVo commodityStockVo = commodityStockDao.findById(commodityStockDto.getId());
        commodityStockRecordItemDto.setStockBefore(commodityStockVo.getStock());
        CommodityStockDto updateDto = new CommodityStockDto();
        updateDto.setId(commodityStockDto.getId());
        updateDto.setStock(commodityStockDto.getQuantity());
        updateDto.setVersion(commodityStockVo.getVersion());
        if (!ObjectUtils.isEmpty(commodityStockDto.getDateOfInventory())) {
            updateDto.setDateOfInventory(commodityStockDto.getDateOfInventory());
        }
        commodityStockRecordItemDto.setStockAfter(updateDto.getStock());
        commodityStockRecordItemDto.setCostPriceBefore(commodityStockVo.getCostPrice());
        commodityStockRecordItemDto.setRetailPriceBefore(commodityStockVo.getRetailPrice());
        // 如果修改了价格，则用修改后的
        if (commodityStockDto.getEtCostPrice().compareTo(BigDecimal.ZERO) > 0) {
            commodityStockRecordItemDto.setCostPriceAfter(commodityStockDto.getEtCostPrice());
            commodityStockRecordItemDto.setCostPrice(commodityStockDto.getEtCostPrice());
            updateDto.setCostPrice(commodityStockDto.getEtCostPrice());
        } else {
            commodityStockRecordItemDto.setCostPriceAfter(commodityStockVo.getCostPrice());
            commodityStockRecordItemDto.setCostPrice(commodityStockVo.getCostPrice());
        }
        if (commodityStockDto.getEtRetailPrice().compareTo(BigDecimal.ZERO) > 0) {
            commodityStockRecordItemDto.setRetailPriceAfter(commodityStockDto.getEtRetailPrice());
            commodityStockRecordItemDto.setRetailPrice(commodityStockDto.getEtRetailPrice());
            updateDto.setEtRetailPrice(commodityStockDto.getEtRetailPrice());
        } else {
            commodityStockRecordItemDto.setRetailPriceAfter(commodityStockVo.getRetailPrice());
            commodityStockRecordItemDto.setRetailPrice(commodityStockVo.getRetailPrice());
        }

        int i = commodityStockDao.update(updateDto);

        if (i < 1) {
            if (num <= 3) {
                this.updateStock(commodityStockDto, commodityStockRecordItemDto, ++num);
            } else {
                throw new Exception("增加库存失败！");
            }
        } else {
            commodityStockRecordItemService.add(commodityStockRecordItemDto);
        }
    }
}
